/*
 * Copyright (c) 2013 Hai Bison
 *
 * See the file LICENSE at the root directory of this project for copying
 * permission.
 */

package com.haibison.android.uyenkha;

import java.security.SecureRandom;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature;
import android.text.TextUtils;

/**
 * Global settings, stored in {@link SharedPreferences}.
 * 
 * @author Hai Bison
 * 
 */
public class Settings {

    /**
     * The UID.
     */
    private static final String PKEY_SECURITY_UID = "security.uid";

    /**
     * Pirate mode.
     */
    private static final String PKEY_SECURITY_PIRATE_MODE = "security.pirate_mode";

    /**
     * This is singleton class.
     */
    private Settings() {
    }// Settings()

    /**
     * Gets global {@link SharedPreferences}.
     * 
     * @param context
     *            the context.
     * @return {@link SharedPreferences}.
     */
    @SuppressLint("InlinedApi")
    public static SharedPreferences p(Context context) {
        /*
         * Always use application context.
         */
        return context.getApplicationContext().getSharedPreferences(
                PirateService.LIB_CODE_NAME + '.' + PirateService.UUID
                        + ".settings", Context.MODE_MULTI_PROCESS);
    }// p()

    /**
     * Gets UID. If not available then a new one will be generated.
     * 
     * @param context
     *            the context.
     * @return the UID.
     */
    public static char[] getUid(Context context) {
        return getUid(p(context));
    }// getUid()

    /**
     * Gets UID. If not available then a new one will be generated.
     * 
     * @param preferences
     *            the preferences.
     * @return the UID.
     */
    private static char[] getUid(SharedPreferences preferences) {
        String result = preferences.getString(PKEY_SECURITY_UID, null);

        if (TextUtils.isEmpty(result)) {
            result = SimpleWeakEncryption.Base36.toBase36(SecureRandom
                    .getSeed(Byte.MAX_VALUE));
            preferences.edit().putString(PKEY_SECURITY_UID, result).commit();
        }

        return result.toCharArray();
    }// getUid()

    /**
     * Gets the <b>unique</b> signature which belongs to this app.
     * 
     * @param context
     *            the context.
     * @return the unique signature.
     */
    public static byte[] getSignature(Context context) {
        final PackageInfo packageInfo;
        try {
            packageInfo = context.getPackageManager().getPackageInfo(
                    context.getPackageName(), PackageManager.GET_SIGNATURES);
        } catch (NameNotFoundException e) {
            /*
             * We should never be here. But in that case, return this one.
             */
            return SimpleWeakEncryption.sha256(context.getPackageName());
        }

        Signature[] signatures = packageInfo.signatures;
        if (signatures == null || signatures.length <= 0)
            return SimpleWeakEncryption.sha256(context.getPackageName());

        /*
         * We should reach this.
         */
        byte[] result = SimpleWeakEncryption
                .sha256(signatures[0].toByteArray());
        for (int i = 0; i < Byte.MAX_VALUE; i++)
            result = SimpleWeakEncryption.sha256(result);
        return result;
    }// getSignature()

    /**
     * Sets a string securely.
     * 
     * @param context
     *            the context.
     * @param key
     *            the key.
     * @param value
     *            the value, can be {@code null}.
     */
    public static void setStringSecurely(Context context, String key,
            String value) {
        SharedPreferences preferences = p(context);
        Editor editor = preferences.edit();

        if (value == null)
            editor.remove(key);
        else
            editor.putString(key, (SimpleWeakEncryption.encrypt(
                    getUid(preferences), getSignature(context), value)));

        editor.commit();
    }// setStringSecurely()

    /**
     * Gets a secure string.
     * 
     * @param context
     *            the context.
     * @param key
     *            the key.
     * @return the string, can be {@code null}.
     */
    public static String getSecureString(Context context, String key) {
        return getSecureString(context, key, null);
    }// getSecureString()

    /**
     * Gets a secure string.
     * 
     * @param context
     *            the context.
     * @param key
     *            the key.
     * @param defValue
     *            the default value.
     * @return the string, or {@code null} if an error occurred while decrypting
     *         data.
     */
    public static String getSecureString(Context context, String key,
            String defValue) {
        String result = p(context).getString(key, null);
        if (result == null)
            return defValue;
        try {
            return SimpleWeakEncryption.decrypt(getUid(context),
                    getSignature(context), result);
        } catch (Throwable t) {
            return null;
        }
    }// getSecureString()

    /**
     * Checks pirate mode.
     * 
     * @param context
     *            the context.
     * @return {@code true} or {@code false}.
     */
    public static boolean isPirateMode(Context context) {
        try {
            return Boolean.TRUE.toString().equals(
                    getSecureString(context, PKEY_SECURITY_PIRATE_MODE));
        } catch (Throwable t) {
            /*
             * If any error occurred, return true.
             */
            return true;
        }
    }// isPirateMode()

    /**
     * Sets pirate mode.
     * 
     * @param context
     *            the context.
     * @param mode
     *            {@code true} or {@code false}.
     */
    public static void setPirateMode(Context context, boolean mode) {
        setStringSecurely(context, PKEY_SECURITY_PIRATE_MODE,
                Boolean.toString(mode));
    }// setPirateMode()

}
